#include "AutoLock.hh"
#include "ChainCore.hh"

ChainCore::ChainCore(Lock* Key)
{
	State = false;
	
	LockOfIO = Key;
	checkNullFV(LockOfIO, "LockOfIO is NULL.");
	
	Tail = NULL;
	Header = NULL;
	NumOfNode = 0;
	
	State = true;
}

ResultOf ChainCore::addHeader(void* Object)
{
	if (!State)
	{
		fprintf(stderr, "This instance(%x) is not available.\n", (Number)this);
		return RETURN_ERROR;
	}
	
	if (Object == NULL)
	{
		notifyError(Object, "Bad parameter");
		return RETURN_PARAM_ERROR;
	}
	
	ResultOf Result;
	Node* NewNode;
	
	NewNode = createNode(Object);
	checkNullWR(NewNode, "NewNode is NULL.");
	
	AutoLock AutoKey(LockOfIO);
	
	Result = replaceHeader(NewNode);
	checkErrorWR(Result, "Fail replaceHeader.");
	
	++NumOfNode;
	return RETURN_OK;
}

ResultOf ChainCore::addTail(void* Object)
{
	if (!State)
	{
		fprintf(stderr, "This instance(%x) is not available.\n", (Number)this);
		return RETURN_ERROR;
	}
	
	if (Object == NULL)
	{
		notifyError(Object, "Bad parameter");
		return RETURN_PARAM_ERROR;
	}
	
	ResultOf Result;
	Node* NewNode;
	
	NewNode = createNode(Object);
	checkNullWR(NewNode, "NewNode is NULL.");
	
	AutoLock AutoKey(LockOfIO);
	
	Result = replaceTail(NewNode);
	checkErrorWR(Result, "Fail changeTail.");
	
	++NumOfNode;
	return RETURN_OK;
}

ResultOf ChainCore::clear()
{
	if (!State)
	{
		fprintf(stderr, "This instance(%x) is not available.\n", (Number)this);
		return RETURN_ERROR;
	}
	
	while (Header != NULL)
		removeHeader();
	
	return RETURN_OK;
}

void* ChainCore::removeHeader()
{
	if (!State)
	{
		fprintf(stderr, "This instance(%x) is not available.\n", (Number)this);
		return NULL;
	}
	
	if (Header == NULL)
		return NULL;
	
	ResultOf Result;
	Node* PrevHeader;
	
	{
		AutoLock AutoKey(LockOfIO);
		
		PrevHeader = Header;
		Result = updateHeader();
		checkErrorWRNull(Result, "Fail updateHeader.");
		
		Result = PrevHeader->unlink();
		checkErrorWRNull(Result, "Fail PrevHeader->unlink.");
		
		--NumOfNode;
	}
	
	void* Object;
	Object = PrevHeader->tellObject();
	delete PrevHeader;
	return Object;
}

void* ChainCore::removeTail()
{
	if (!State)
	{
		fprintf(stderr, "This instance(%x) is not available.\n", (Number)this);
		return NULL;
	}
	
	if (Tail == NULL)
		return NULL;
	
	Node* PrevTail;
	ResultOf Result;
	
	{
		AutoLock AutoKey(LockOfIO);
		
		PrevTail = Tail;
		Result = updateTail();
		checkErrorWRNull(Result, "Fail updateTail.");
		
		Result = PrevTail->unlink();
		checkErrorWRNull(Result, "Fail PrevTail->unlink.");
		
		--NumOfNode;
	}
	
	void* Object;
	Object = PrevTail->tellObject();
	delete PrevTail;
	return Object;
}

USNumber ChainCore::tellSize()
{
	if (!State)
	{
		fprintf(stderr, "This instance(%x) is not available.\n", (Number)this);
		return 0;
	}
	
	return NumOfNode;
}

USNumber ChainCore::tellSizeWithSync()
{
	AutoLock AutoKey(LockOfIO);
	return NumOfNode;
}

Node* ChainCore::createNode(void* Object)
{
	ResultOf Result;
	Node* NewNode;
	
	// NOTE
	// 체인은 각 노드들이 양방향으로 연결되는 자료구조를 말한다.
	// 즉, 항상 양방향(doubly)으로 연결되는 노드를 사용한다.
	
	NewNode = Node::generate(DoublyNodeType);
	checkNullWRNull(NewNode, "NewNode is NULL.");
	
	Result = NewNode->attach(Object);
	if (checkError(Result))
	{
		notifyError(Result, "Fail NewNode->attach.");
		delete NewNode;
		NewNode = NULL;
	}
	
	return NewNode;
}

ResultOf ChainCore::replaceHeader(Node* NewNode)
{
	ResultOf Result;
	if (Header != NULL)
	{
		Result = Header->linkLeft(NewNode);
		checkErrorWR(Result, "Fail Header->linkLeft.");
	}
	
	Header = NewNode;
	if (Tail == NULL)
		Tail = Header;
	
	return RETURN_OK;
}

ResultOf ChainCore::replaceTail(Node* NewNode)
{
	ResultOf Result;
	if (Tail != NULL)
	{
		Result = Tail->linkRight(NewNode);
		checkErrorWR(Result, "Fail Tail->linkRight.");
	}
	
	Tail = NewNode;
	if (Header == NULL)
		Header = Tail;
	
	return RETURN_OK;
}

ResultOf ChainCore::updateHeader()
{
	if (Header != Tail)
	{
		Header = Header->tellRight();
	}
	else
	{
		Tail = NULL;
		Header = NULL;
	}
	
	return RETURN_OK;
}

ResultOf ChainCore::updateTail()
{
	if (Tail != Header)
	{
		Tail = Tail->tellLeft();
	}
	else
	{
		Tail = NULL;
		Header = NULL;
	}
	
	return RETURN_OK;
}
